package com.xxpt.common.aspectj;

import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.xxpt.common.constant.UserContext;
import com.xxpt.common.enums.DeviceTypeEnum;
import com.xxpt.common.utils.Tools;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.MDC;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.regex.Pattern;

@Slf4j
@Aspect
@Order(value = -1000) // 让这个切面最先执行
@Component
public class RequestAspect {

    @Pointcut(value = "execution(* com.xxpt.*.controller..*.*(..))")
    public void valMethod() {

    }

    @Before("valMethod()")
    public void before(JoinPoint point) {
        UserContext.init();
        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes) ra;
        if (sra == null) {
            return;
        }
        HttpServletRequest request = sra.getRequest();
        if (request == null) {
            return;
        }

        //请求参数信息
        try {
            StringBuffer url = request.getRequestURL();
            if (request.getQueryString() != null) {
                url.append('?');
                url.append(request.getQueryString());
            }
            String path = point.getTarget().getClass().getName();
            String mName = point.getSignature().getName();
            String argStr = getArgs(point.getArgs(), mName);
            log.info("--------------------------------------------------------\n" +
                            ">>>>header:{} >>>>>请求IP:{} 方法:{} >>>>>请求路径:{},参数如下↓↓↓↓ {}",
                    getHeadersInfo(request),
                    Tools.getIpAddr(request),
                    (path + "." + mName + "()"),
                    url,
                    argStr + "\n");
        } catch (Exception e) {
            log.error("异常方法:{}异常", point.getTarget().getClass().getName() + point.getSignature().getName(), e);
        }

        dataPrepare(request);
        // 获取RequestId,如果为空则不放入日志MDC中
        String requestId = request.getHeader(UserContext.REQUEST_ID_KEY);
        if (StringUtils.isBlank(requestId)) {
            return;
        }
        MDC.put(UserContext.REQUEST_ID_KEY, requestId);
        UserContext.put(UserContext.REQUEST_ID_KEY, requestId);
    }

    /**
     * 方法之后调用 清除线程缓存
     */
    @AfterReturning(value = "valMethod()")
    public void doAfterReturning() {
        UserContext.remove();
        MDC.remove(UserContext.REQUEST_ID_KEY);
    }

    /**
     * 方法抛出异常后 清除线程缓存
     */
    @AfterThrowing(value = "valMethod()")
    public void doAfterThrowing() {
        UserContext.remove();
    }

    /**
     * 参数解析
     * @param request
     */
    public static void dataPrepare(HttpServletRequest request) {
        String userToken = request.getHeader(UserContext.AUTHORIZE_TOKEN);
        userToken = userToken == null ? "" : userToken;
        String language = request.getHeader(UserContext.LANGUAGE_KEY);
        String gender = request.getHeader(UserContext.GENDER_KEY);
        String deviceType = request.getHeader(UserContext.DEVICE_TYPE_KEY);

        UserContext.put(UserContext.DEVICE_TYPE_KEY, deviceType);
        UserContext.put(UserContext.LANGUAGE_KEY, language);
        UserContext.put(UserContext.AUTHORIZE_TOKEN, userToken);
        UserContext.put(UserContext.GENDER_KEY, gender);

        // 获取UserAgent信息
        Map<String, String> deviceChannelVersionFromUserAgent = getDeviceChannelVersionFromUserAgent(request);
        userAgentMapToUserContext(deviceChannelVersionFromUserAgent);
    }

    private static void userAgentMapToUserContext(Map<String, String> userAgentMap) {
        if (userAgentMap == null) {
            return;
        }
        UserContext.put(UserContext.USER_AGENT_OS_KEY, userAgentMap.get(UserContext.USER_AGENT_OS_KEY));
        UserContext.put(UserContext.USER_AGENT_OS_VERSION_KEY, userAgentMap.get(UserContext.USER_AGENT_OS_VERSION_KEY));
        UserContext.put(UserContext.USER_AGENT_VERSION_KEY, userAgentMap.get(UserContext.USER_AGENT_VERSION_KEY));
        UserContext.put(UserContext.USER_AGENT_MODEL_KEY, userAgentMap.get(UserContext.USER_AGENT_MODEL_KEY));
        UserContext.put(UserContext.USER_AGENT_SOURCE_KEY, userAgentMap.get(UserContext.USER_AGENT_SOURCE_KEY));
        String os = (String) UserContext.get(UserContext.USER_AGENT_OS_KEY);
        os = os == null ? "" : os;
        if (os.equalsIgnoreCase(DeviceTypeEnum.IOS.getName())) {
            UserContext.put(UserContext.DEVICE_TYPE_KEY, DeviceTypeEnum.IOS.getId().toString());
        } else if (os.equalsIgnoreCase(DeviceTypeEnum.ANDROID.getName())) {
            UserContext.put(UserContext.DEVICE_TYPE_KEY, DeviceTypeEnum.ANDROID.getId().toString());
        }
    }

    private static Map<String, String> getDeviceChannelVersionFromUserAgent(HttpServletRequest request) {
        String userAgent = request.getHeader("UserAgent");
        Map<String, String> resultMap = new HashMap<>();
        try {
            // userAgent = "os/IOS|channel/appstore|version/1.0|model/iPhone Xʀ|build/1|osVersion/14.3|source/dou"
            if (StringUtils.isNotBlank(userAgent)) {
                String[] userAgents = userAgent.split("\\|");
                for (String agent : userAgents) {
                    if (StringUtils.isNotBlank(agent)){
                        String[] split = agent.split("/");
                        if (split.length >= 2){
                            resultMap.put(split[0], split[1]);
                        }
                    }
                }
            }
        } catch (Exception e) {
            log.info("解析userAgent异常 异常可以暂时忽略,不影响业务执行 userAgent: " + userAgent +" ", e);
        }
        return resultMap;
    }

    /**
     * 获取参数
     */
    private String getArgs(Object[] args, String mName) throws Exception {
        if (args == null) {
            return null;
        }
        String className = null;
        int index = 1;

        List<String> argList = new ArrayList<>();
        for (Object arg : args) {
            if (arg == null) {
                continue;
            }
            className = arg.getClass().getName();
            className = className.substring(className.lastIndexOf(".") + 1);
            //屏蔽参数
            if (Pattern.compile("AuthProperties|MultipartFile").matcher(className).find()) {
                continue;
            }
            if ((className.equals("HashMap") && ((Map) arg).size() > 100)
                    || className.equals("ArrayList") && ((List) arg).size() > 100) {
                argList.add(">>>>>参数" + index + "(" + className + ")(略)");
            } else {
                argList.add(">>>>>参数" + index + "(" + className + "):" + JSON.toJSONString(arg));
            }
            index++;
        }
        return StringUtils.join(argList,"\n");
    }

    private String getHeadersInfo(HttpServletRequest request) {
        Map<String, String> map = new HashMap<>();
        Enumeration<String> headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String key = (String) headerNames.nextElement();
            String value = request.getHeader(key);
            map.put(key, value);
        }
        return JSONUtil.toJsonStr(map);
    }
}
